﻿/********************************************************************************
* Copyright 2010 Zane Thorn (zane.thorn@gmail.com)                              *
*                                                                               *
* NeturalMath is free software: you can redistribute it and/or modify           *
* it under the terms of the GNU Lesser General Public License as published by   *
* the Free Software Foundation, either version 3 of the License, or             *
* (at your option) any later version.                                           *
*                                                                               *
* NeturalMath is distributed in the hope that it will be useful,                *
* but WITHOUT ANY WARRANTY; without even the implied warranty of                *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 *
* GNU Lesser General Public License for more details.                           *
*                                                                               *
* You should have received a copy of the GNU Lesser General Public License      *
* along with NeturalMath.  If not, see <http://www.gnu.org/licenses/>.          *
********************************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NeturalMath.Expressions;
using NeturalMath.Properties;

namespace NeturalMath
{
    /// <summary>
    /// Built in data type to represent a set of values
    /// </summary>
    public class SetValue:MathValue,IEnumerable<ValueExpression>
    {
        #region Constructor

        /// <summary>
        /// Creates a new instance of the set value type
        /// </summary>
        /// <param name="values"></param>
        internal SetValue(IEnumerable<ValueExpression> values,MathRuntime runtime) 
            : base(ValueTypes.Set, "set", values,runtime)
        {
            InnerValue = values.ToArray();
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Overrides the base value with a value appropriate to this data type
        /// </summary>
        public new IEnumerable<ValueExpression> Value { get { return InnerValue; } }
        protected ValueExpression[] InnerValue { get; private set; }
        public IEnumerable<MathValue> Results { get { return InnerValue.Select(v => v.Execute()); } }

        #endregion

        #region Helper Methods

        #region Basic Math Functions

        /// <summary>
        /// Adds two values together
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        protected override MathValue Add(MathValue arg)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Subtracts the argument from the present value
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        protected override MathValue Subtract(MathValue arg)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Multiplies two values together
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        protected override MathValue Multiply(MathValue arg)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Divides two values
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        protected override MathValue Divide(MathValue arg)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Performs modulus division on two values
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        protected override MathValue Modulo(MathValue arg)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Raises the value to the power provided
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        protected override MathValue Power(MathValue arg)
        {
            throw new NotImplementedException();
        }


        #endregion

        #region Logical Functions

        /// <summary>
        /// Performs an AND operation on the value
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        protected override MathValue And(MathValue arg)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Performs an OR operation on the value
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        protected override MathValue Or(MathValue arg)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Performsn an XOR operaton on the values
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        protected override MathValue Xor(MathValue arg)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region Relational Functions

       
        /// <summary>
        /// Determines if this value is less than the argument provided
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        protected override bool? IsLessThan(MathValue arg)
        {
            throw new NotImplementedException();
        }

        
        /// <summary>
        /// Determines if the value is greater than  to the value provided
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        protected override bool? IsGreaterThan(MathValue arg)
        {
            throw new NotImplementedException();
        }


        #endregion

        #region Conversion Functions
        
        /// <summary>
        /// Flips the order of items in the Set
        /// </summary>
        /// <returns></returns>
        protected override MathValue Flip()
        {
            return Runtime.NewSet(Value.Reverse());
        }

        /// <summary>
        /// Attempts to convert the value to true
        /// </summary>
        /// <returns></returns>
        protected override bool IsTrue()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Attempts to convert the value to a negative
        /// </summary>
        /// <returns></returns>
        protected override MathValue ToMinus()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Converts the current value to a string
        /// </summary>
        /// <returns>The string value of the data</returns>
        protected internal override MathValue ConvertToString()
        {
            if (InnerValue.Length == 1)
            {
                return InnerValue[0].Execute().ConvertToString();
            }
            throw new MathException(ErrorCodes.SetCouldNotConvertToScalar, Resources.SetCouldNotConvertToScalar);
        }

        /// <summary>
        /// Converts a current value to a number
        /// </summary>
        /// <returns>The numeric value of the data</returns>
        protected internal override MathValue ConvertToNumber()
        {
            if (InnerValue.Length==1)
            {
                return InnerValue[0].Execute().ConvertToNumber();
            }
            throw new MathException(ErrorCodes.SetCouldNotConvertToScalar, Resources.SetCouldNotConvertToScalar);
        }

        /// <summary>
        /// Converts the current value to a boolean
        /// </summary>
        /// <returns></returns>
        protected internal override MathValue ConvertToBoolean()
        {
            if (InnerValue.Length == 1)
            {
                return InnerValue[0].Execute().ConvertToBoolean();
            }
            throw new MathException(ErrorCodes.SetCouldNotConvertToScalar, Resources.SetCouldNotConvertToScalar);
        }

        /// <summary>
        /// Converts the argument into the present data type
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        protected override MathValue ConvertToNativeValue(MathValue arg)
        {
            return arg.ConvertToSet();
        }

        /// <summary>
        /// Converts the value to a range
        /// </summary>
        /// <returns></returns>
        protected internal override MathValue ConvertToRange()
        {
            if (InnerValue.Length == 1)
            {
                return InnerValue[0].Execute().ConvertToRange();
            }
            else if (InnerValue.Length==2)
            {
                var upper = (NumberValue)InnerValue[1].Execute().ConvertToNumber();
                var lower = (NumberValue)InnerValue[0].Execute().ConvertToNumber();
                return Runtime.NewRange(lower, upper);
            }
            throw new MathException(ErrorCodes.SetCouldNotConvertToRange, Resources.SetCouldNotConvertToScalar);
        }

        /// <summary>
        /// Converts the value to a complex
        /// </summary>
        /// <returns></returns>
        protected internal override MathValue ConvertToComplex()
        {
            if (InnerValue.Length == 1)
            {
                return InnerValue[0].Execute().ConvertToComplex();
            }
            else if (InnerValue.Length == 2)
            {
                var i = (NumberValue)InnerValue[1].Execute().ConvertToNumber();
                var real = (NumberValue)InnerValue[0].Execute().ConvertToNumber();
                return Runtime.NewComplex(real, i);
            }
            throw new MathException(ErrorCodes.SetCouldNotConvertToComplex, Resources.SetCouldNotConvertToScalar);
        }

        /// <summary>
        /// Converts the value to a unit, based on the specified unit measure
        /// </summary>
        /// <param name="measure"></param>
        /// <returns></returns>
        protected internal override MathValue ConvertToUnit(Units.UnitMeasure measure)
        {
            if (InnerValue.Length == 1)
            {
                return InnerValue[0].Execute().ConvertToUnit(measure);
            }
            throw new MathException(ErrorCodes.SetCouldNotConvertToScalar, Resources.SetCouldNotConvertToScalar);
        }

        #endregion

        /// <summary>
        /// Gets a string representation of the data
        /// </summary>
        /// <returns></returns>
        protected override string GetToString()
        {
            var builder = new StringBuilder();

            builder.Append("[");
            for (int i =0;i<InnerValue.Length;i++)
            {
                if (i < 0)
                    builder.Append(", ");
                builder.Append(InnerValue[i]);
            }
            builder.Append("]");

            return builder.ToString();
        }

        #endregion

        #region IEnumerable<MathValue> Members

        public IEnumerator<ValueExpression> GetEnumerator()
        {
            return Value.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return Value.GetEnumerator();
        }

        #endregion
    }
}
